package com.cenntro.common.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

import org.apache.commons.lang3.reflect.FieldUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Queues;
import com.google.common.collect.Sets;

/**
 * 通用Convertor
 * 
 */
public class ConvertorUtils {
    private static Logger                                 logger                  = LoggerFactory
        .getLogger(ConvertorUtils.class);
    /**
     * 转化器的上下文
     */
    private static final ThreadLocal<Map<Object, Object>> context                 = new ThreadLocal<Map<Object, Object>>();
    /**
     * field的缓存
     */
    private static final Map<Class<?>, List<Field>>       classFeildsCache        = Maps.newConcurrentMap();
    /**
     * field的缓存
     */
    private static final Map<String, Field>               propertyFeildCache      = Maps.newConcurrentMap();
    /**
     * 属性描述器缓存
     */
    private static final Map<Field, PropertyDescriptor>   propertyDescriptorCache = Maps.newConcurrentMap();

    /**
     * 不可实例化
     */
    private ConvertorUtils() {
    }

    /**
     * 获取所有Field (包含上级)
     * 
     * @param targetClass
     * @return
     * @date: 2016年1月28日 上午10:49:27
     */
    public static List<Field> getDeclaredFields(Class<?> clazz) {
        if (clazz == null) {
            return null;
        }
        List<Field> allFields = classFeildsCache.get(clazz);
        if (allFields == null) {
            allFields = Collections.synchronizedList(new ArrayList<Field>());
            for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
                for (Field field : clazz.getDeclaredFields()) {
                    // 静态字段、final字段跳过
                    if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) {
                        continue;
                    }
                    allFields.add(field);
                }
            }
        }
        return allFields;
    }

    /**
     * 获取Field : 优先取本类声明的field，没有则遍历父类寻找
     * 
     * @param targetClass
     * @return
     * @date: 2016年1月28日 上午10:49:27
     */
    public static Field getDeclaredField(Class<?> clazz, String propertyName) {
        if (clazz == null || propertyName == null || propertyName.isEmpty()) {
            return null;
        }
        String key = clazz.getName() + "." + propertyName;
        Field field = propertyFeildCache.get(key);
        if (field == null) {
        	field = FieldUtils.getField(clazz, propertyName, true);
//            try {
//                field = clazz.getDeclaredField(propertyName);
//            } catch (NoSuchFieldException e) {
//                if (logger.isDebugEnabled()) {
//                    logger.debug("NoSuchFieldException : {}.{}", clazz.getName(), propertyName, e);
//                }
//            } catch (SecurityException e) {
//                if (logger.isDebugEnabled()) {
//                    logger.debug("SecurityException : {}.{}", clazz.getName(), propertyName, e);
//                }
//            }
            if (field == null && Object.class != clazz.getSuperclass()) {
                //field = getDeclaredField(clazz.getSuperclass(), propertyName);
                field = FieldUtils.getField(clazz.getSuperclass(), propertyName, true);
            }
            if (key != null && field != null) {
                propertyFeildCache.put(key, field);
            }
        }
        return field;
    }

    /**
     * 解决线程未释放，context当前线程map中对象堆积，导致内存溢出的bug
     * @param sourceObject
     * @param targetClass
     * @return
     * @date: 2016年5月9日 上午9:34:59
     */
    public static <T> T deepConvert(Object sourceObject, Class<T> targetClass) {
        T t = doDeepConvert(sourceObject, targetClass);
        context.remove();
        return t;
    }

    /**
     * 深度类型转换:
     * <p>
     * 1.支持无限树结构; 2.支持同名属性，不同类，自动转化; 3.支持Collection类型属性，自动遍历转化；
     * </p>
     * 
     * @param sourceObject
     * @param targetClass
     * @return
     * @date: 2015年12月14日 下午2:37:52
     */
    @SuppressWarnings("unchecked")
    public static <T> T doDeepConvert(Object sourceObject, Class<T> targetClass) {
        Map<Object, Object> contextMap = context.get();
        if (contextMap == null) {
            contextMap = Maps.newHashMap();
            context.set(contextMap);
        }
        // 该对象已经转化，直接返回,用于解决循环引用的导致的栈溢出
        if (contextMap.containsKey(sourceObject)) {
            Object target = contextMap.get(sourceObject);
            if (target != null && target.getClass() == targetClass) {
                return (T) target;
            } else {
                contextMap.remove(sourceObject);
            }
        }

        if (sourceObject == null) {
            return null;
        }
        T targetObject = null;
        try {
            targetObject = targetClass.newInstance();
            contextMap.put(sourceObject, targetObject);
            for (Field field : getDeclaredFields(targetClass)) {
                // 静态字段、final字段跳过
                Field sourceField = getDeclaredField(sourceObject.getClass(), field.getName());
                if (sourceField == null) {
                    continue;
                }
                Object sourceProperty = getProperty(sourceObject, sourceField.getName());
                if (sourceProperty == null) {
                    continue;
                }
                Class<?> fieldClass = field.getType();
                Class<?> srcFieldClass = sourceField.getType();
                // #TODO: 暂不支持泛型
                // 均是数组，且数组中元素不一致
                if (fieldClass.isArray() && srcFieldClass.isArray()) {
                    boolean isSameType = fieldClass.getComponentType().equals(srcFieldClass.getComponentType());
                    int length = Array.getLength(sourceProperty);
                    Object targetPropArr = Array.newInstance(fieldClass.getComponentType(), length);
                    for (int i = 0; i < length; i++) {
                        Object _sourceProperty = Array.get(sourceProperty, i);
                        if (isSameType) {
                            Array.set(targetPropArr, i, _sourceProperty);
                        } else {
                            Array.set(targetPropArr, i, doDeepConvert(_sourceProperty, fieldClass.getComponentType()));
                        }
                    }
                    setProperty(targetObject, field.getName(), targetPropArr);
                }
                // 容器且容器中泛型类型不一致
                else if (Collection.class.isAssignableFrom(fieldClass)) {
                    Class<?> targetGenericClass = getFieldGenericClass(field);
                    Class<?> sourceGenericClass = getFieldGenericClass(sourceField);
                    Collection<?> _sourceCollection = (Collection<?>) sourceProperty;
                    Collection<Object> _targetCollection = null;
                    if (targetGenericClass != null && targetGenericClass != Object.class
                        && targetGenericClass != sourceGenericClass) {
                        _targetCollection = (Collection<Object>) deepConvertCollection(_sourceCollection,
                            targetGenericClass);
                    } else {
                        if (!(Modifier.isAbstract(fieldClass.getModifiers())
                              && Modifier.isInterface(fieldClass.getModifiers()))) {
                            _targetCollection = (Collection<Object>) fieldClass.newInstance();
                        } else if (List.class.isAssignableFrom(fieldClass)) {
                            _targetCollection = Lists.newArrayList();
                        } else if (Set.class.isAssignableFrom(fieldClass)) {
                            _targetCollection = Sets.newHashSet();
                        } else if (Queue.class.isAssignableFrom(fieldClass)) {
                            _targetCollection = Queues.newLinkedBlockingQueue();
                        }
                        Iterator<?> iter = _sourceCollection.iterator();
                        while (_targetCollection != null && iter.hasNext()) {
                            _targetCollection.add(iter.next());
                        }
                    }
                    setProperty(targetObject, field.getName(), _targetCollection);
                }
                // 属性的类型不匹配
                else if (!field.getType().equals(sourceField.getType())) {
                    Object targetProperty = doDeepConvert(sourceProperty, field.getType());
                    setProperty(targetObject, field.getName(), targetProperty);
                } else {
                    setProperty(targetObject, field.getName(), sourceProperty);
                }
            }

        } catch (InstantiationException e) {
            throw new RuntimeException("对象转换异常:" + sourceObject.getClass() + " -> " + targetClass, e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("对象转换异常:" + sourceObject.getClass() + " -> " + targetClass, e);
        }

        return targetObject;
    }

    /**
     * list深度转化
     * 
     * @param sourceObject
     * @param targetClass
     * @return
     * @date: 2016年3月3日 上午9:25:00
     */
    public static <T> List<T> deepConvertList(List<?> sourceObjects, Class<T> targetClass) {
        if (sourceObjects == null) {
            return null;
        }
        List<T> targetList = Lists.newArrayList();
        for (Object sourceObject : sourceObjects) {
            targetList.add(deepConvert(sourceObject, targetClass));
        }
        return targetList;
    }

    /**
     * set深度转化
     * 
     * @param sourceObject
     * @param targetClass
     * @return
     * @date: 2016年3月3日 上午9:25:13
     */
    public static <T> Set<T> deepConvertSet(Set<?> sourceObjects, Class<T> targetClass) {
        if (sourceObjects == null) {
            return null;
        }
        Set<T> targetSet = Sets.newHashSet();
        for (Object sourceObject : sourceObjects) {
            targetSet.add(deepConvert(sourceObject, targetClass));
        }
        return targetSet;
    }

    /**
     * 容器类型的转化
     * 
     * @param sourceObject
     * @param targetClass
     * @return
     * @date: 2016年3月3日 上午9:26:30
     */
    public static <T> Collection<T> deepConvertCollection(Collection<?> sourceObject, Class<T> targetClass) {
        Collection<T> collection = null;
        if (sourceObject instanceof List) {
            collection = deepConvertList((List<?>) sourceObject, targetClass);
        } else if (sourceObject instanceof Set) {
            collection = deepConvertSet((Set<?>) sourceObject, targetClass);
        }
        return collection;
    }

    /**
     * 取得容器泛型类
     * 
     * @param field
     * @return
     * @date: 2016年3月2日 下午8:46:15
     */
    public static Class<?> getFieldGenericClass(Field field) {
        if (!Collection.class.isAssignableFrom(field.getType())) {
            return field.getType();
        }
        Type type = field.getGenericType();
        if (type instanceof ParameterizedType) {
            ParameterizedType paramType = (ParameterizedType) type;
            Type[] actualTypes = paramType.getActualTypeArguments();
            for (Type aType : actualTypes) {
                if (aType instanceof Class) {
                    return (Class<?>) aType;
                }
            }
        }
        return Object.class;
    }

    /**
     * 获取属性值
     * 
     * @param sourceObject
     * @param propertyName
     * @return
     * @throws BeansException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     * @date: 2016年3月2日 下午8:09:16
     */
    public static Object getProperty(Object sourceObject, String propertyName) {
        if (sourceObject == null) {
            return null;
        }
        PropertyDescriptor propertyDescriptor = getPropertyDescriptor(sourceObject.getClass(), propertyName);
        if (propertyDescriptor == null || propertyDescriptor.getReadMethod() == null) {
            return null;
        }
        Object value = null;
        try {
            value = propertyDescriptor.getReadMethod().invoke(sourceObject);
        } catch (IllegalAccessException e) {
            logger.info("get " + sourceObject.getClass() + "." + propertyName + "  value=" + value + " info...", e);
        } catch (IllegalArgumentException e) {
            logger.info("get " + sourceObject.getClass() + "." + propertyName + "  value=" + value + " info...", e);
        } catch (InvocationTargetException e) {
            logger.info("get " + sourceObject.getClass() + "." + propertyName + "  value=" + value + " info...", e);
        }
        return value;
    }

    /**
     * 设置属性值
     * 
     * @param sourceObject
     * @param propertyName
     * @param value
     * @throws BeansException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     * @date: 2016年3月2日 下午8:09:34
     */
    public static void setProperty(Object sourceObject, String propertyName, Object value) {
        PropertyDescriptor propertyDescriptor = getPropertyDescriptor(sourceObject.getClass(), propertyName);
        if (propertyDescriptor == null || propertyDescriptor.getWriteMethod() == null) {
            return;
        }
        try {
            propertyDescriptor.getWriteMethod().invoke(sourceObject, new Object[] { value });
        } catch (IllegalAccessException e) {
            logger.info("set " + sourceObject.getClass() + "." + propertyName + "  value=" + value + " info...", e);
        } catch (IllegalArgumentException e) {
            logger.info("set " + sourceObject.getClass() + "." + propertyName + "  value=" + value + " info...", e);
        } catch (InvocationTargetException e) {
            logger.info("set " + sourceObject.getClass() + "." + propertyName + "  value=" + value + " info...", e);
        }
    }

    /**
     * 获取属性描述符
     * 
     * @param clazz
     * @param propertyName
     * @return
     * @date: 2016年3月3日 上午9:55:25
     */
    public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String propertyName) {
        Field field = getDeclaredField(clazz, propertyName);
        if (field == null) {
            return null;
        }
        PropertyDescriptor pd = propertyDescriptorCache.get(field);
        if (pd != null) {
            return pd;
        }
        // 构建方法的后缀
        String methodEnd = propertyName.length() == 1 ? propertyName.toUpperCase()
            : propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
        Method setMethod = null;
        Method getMethod = null;
        // 构建set方法
        try {
            setMethod = clazz.getMethod("set" + methodEnd, new Class[] { field.getType() });
        } catch (SecurityException e) {
            logger.info("get setter for " + clazz + "." + propertyName + " failed...", e);
        } catch (NoSuchMethodException e) {
            logger.info("get setter for " + clazz + "." + propertyName + " failed...", e);
        }
        // 构建get 方法
        try {
            getMethod = clazz.getMethod("get" + methodEnd, new Class[] {});
        } catch (SecurityException e) {
            logger.info("get getter for " + clazz + "." + propertyName + " failed...", e);
        } catch (NoSuchMethodException e) {
            logger.info("get getter for " + clazz + "." + propertyName + " failed...", e);
        }
        // 构建一个属性描述器 把对应属性 propertyName 的 get 和 set 方法保存到属性描述器中
        try {
            pd = new PropertyDescriptor(propertyName, getMethod, setMethod);
            propertyDescriptorCache.put(field, pd);
        } catch (IntrospectionException e) {
            logger.info("get PropertyDescriptor for " + clazz + "." + propertyName + " failed...", e);
        }
        return pd;
    }

    public static <T> T transMap2Bean(Map<String, Object> map, Class<T> targetClass) {
    	T t = null;
    	try {
    		t = targetClass.newInstance();
            BeanInfo beanInfo = Introspector.getBeanInfo(targetClass);
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();
                if (map.containsKey(key)) {
                    Object value = map.get(key);
                    // 得到property对应的setter方法
                    Method setter = property.getWriteMethod();
                    setter.invoke(t, value);
                }
            }
        } catch (Exception e) {
            System.out.println("transMap2Bean Error " + e);
        }
        return t;
    }
}
